package com.szyt.basicframe.aspect;

import com.alibaba.fastjson.JSON;
import com.szyt.basicframe.annotation.OperationLog;
import com.szyt.basicframe.pojo.entity.SysOperationLog;
import com.szyt.basicframe.service.ISysOperationLogService;
import com.szyt.basicframe.util.IpUtil;
import com.szyt.basicframe.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class OperationLogAspect {
    private final HttpServletRequest request;

    private final ISysOperationLogService operateLogService; // 日志服务

    private final ThreadPoolTaskExecutor taskExecutor; // 线程池


    /**
     * 定义切点：标注了@OperateLog注解的方法
     */
    @Pointcut("@annotation(com.szyt.basicframe.annotation.OperationLog)")
    public void operateLogPointCut() {}

    /**
     * 环绕通知
     */
    @Around("operateLogPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取操作日志注解
        OperationLog operateLog = method.getAnnotation(OperationLog.class);

        // 构建日志对象
        SysOperationLog operateLogDO = SysOperationLog.builder()
                .module(operateLog.module())
                .type(operateLog.type())
                .description(operateLog.desc())
                .requestUrl(request.getRequestURI())
                .requestMethod(request.getMethod())
                .operator(SecurityUtils.getCurrentUserId()) // 获取当前用户
                .ip(IpUtil.getIpAddress(request))
                .operationTime(LocalDateTime.now())
                .build();

        // 记录请求参数
        try {
            operateLogDO.setRequestParams(JSON.toJSONString(joinPoint.getArgs()));
        } catch (Exception e) {
            operateLogDO.setRequestParams("参数解析失败");
        }

        long startTime = System.currentTimeMillis();
        Object result;
        try {
            // 执行目标方法
            result = joinPoint.proceed();
            long endTime = System.currentTimeMillis();

            // 记录成功日志
            operateLogDO.setCostTime(endTime - startTime);
            operateLogDO.setStatus(1);
            operateLogService.save(operateLogDO);
        } catch (Exception e) {
            long endTime = System.currentTimeMillis();
            // 记录失败日志
            operateLogDO.setCostTime(endTime - startTime);
            operateLogDO.setStatus(0);
            taskExecutor.execute(() -> {
                try {
                    operateLogService.save(operateLogDO);
                } catch (Exception e2) {
                    log.error("异步保存操作日志失败", e2);
                }
            });

            throw e;
        }
        return result;
    }
}
